package it.ap.sa.acqua.auth.policy.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import it.ap.sa.acqua.auth.service.UserService;
import it.ap.sa.acqua.auth.service.beans.CustomAuthenticationUserDetails;

/**
 * HandlerInterceptor - un'interfaccia che deve essere implementata dalle classi interceptor di Spring, ha i seguenti tre metodi:
 *   - preHandle (...) - chiamato prima del controllor
 *   - postHandle (...) - chiamato subito dopo il controller
 *   - afterCompletion (...) - chiamato subito prima di inviare la risposta alla view
 * La classe Interceptor deve essere dichiarata nel spring context xml all'interno del tag <MVC: interceptor>
 * Interceptor può essere configurato per eseguire in due modi, eseguito per tutte le richieste o mappato per URL specifici.
 * Se viene restituito vero, l'esecuzione continua, per falso l'esecuzione si ferma per tale richiesta.
 * 
 * <mvc:interceptors>
 *   <mvc:interceptor>
 *      <mvc:mapping path="/somepath_one/*"/>
 *      <ref bean="customRequestHandler_one" />
 *   </mvc:interceptor>
 * </mvc:interceptors>
 * 
 * @author marianna
 */
@Component
public class RequestTrackingInterceptor extends HandlerInterceptorAdapter {
	
	public static Logger logger = LoggerFactory.getLogger(RequestTrackingInterceptor.class.getName());
	
	@Autowired
	private UserService userService;
	
	
	private String requestTrackingDiscard;
	private String[] trackingDiscardUris;
	

	/**
	 * Intercept the execution of a handler. Called after HandlerMapping determined an appropriate handler object, but before HandlerAdapter 
	 * invokes the handler.
	 * DispatcherServlet processes a handler in an execution chain, consisting of any number of interceptors, with the handler itself at the end. 
	 * With this method, each interceptor can decide to abort the execution chain, typically sending a HTTP error or writing a custom response.
	 * 
	 * Parameters:
	 *   request - current HTTP request
	 *   response - current HTTP response
	 *   handler - chosen handler to execute, for type and/or instance evaluation
	 *   
	 * Returns:
	 *  true if the execution chain should proceed with the next interceptor or the handler itself. 
	 *  Else, DispatcherServlet assumes that this interceptor has already dealt with the response itself.
	 *  
	 * Throws:
	 *  Exception - in case of errors

	 * Ad ogni request viene controllato se l'utente autenticato deve essere forzato a compiere qualche operazione, come ad esempio:
	 * riloggarsi, un cambio password, ecc...
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

		if (request != null && request instanceof HttpServletRequest) {
			
			HttpServletRequest httpServletRequest = (HttpServletRequest) request;
			
			boolean log = this.canLog(httpServletRequest);

			if(log)
				logger.info("---Before Controller Execution---------------------------------------------------------");
			
			/** recupero i dati della sessione **/
			HttpSession session = httpServletRequest.getSession();

			if(session != null && session.getId() != null){
				/** Recupero i dati dell'utente autenticato */
				CustomAuthenticationUserDetails customUserDetails = userService.getPrincipal();
				
				/** Controllo se e' stato recuperato l'utente autenticato dal token **/
				if(customUserDetails != null){
					
					// utete trovato
					if(log)
						logger.info("Utente autenticato.");
				}else {
					// utete non trovato
					if(log)
						logger.info("Utente autenticato non trovato.");
					return false;
				}
			}else
			   // sessione non trovata
				if(log)
					logger.warn("Sessione non trovata.");
			
			if(log)
				logger.info("---------------------------------------------------------------------------------------");
		}else
		   // else request != null && request instanceof HttpServletRequest
			logger.warn("Request non valorizzata.");
		
		
		return true;
	}
	
	@Override
	public void postHandle(	HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
		if(this.canLog(request))
			logger.info("---Controller executed---");
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
		if(this.canLog(request))
			logger.info("---Request Completed---");
	}

	/**
	 * TODO SAREBBE BELLO SAPERE A COSA SERVE!!
	 */
	private boolean canLog(HttpServletRequest request) {
		String uri = request.getRequestURI();
		boolean log = true;
		if(this.trackingDiscardUris != null && trackingDiscardUris.length > 0){
			for(int i = 0 ; i < trackingDiscardUris.length && log ; i++){
				log = !uri.contains(this.trackingDiscardUris[i]);
			}
		}
		return log;
	}
	/**
	 * TODO SAREBBE BELLO SAPERE A COSA SERVE!!
	 * 
	 * @param requestTrackingDiscard the requestTrackingDiscard to set
	 */
	@Value("${request.tracking.discard:@null}")
	public void setRequestTrackingDiscard(String requestTrackingDiscard) {
		this.requestTrackingDiscard = requestTrackingDiscard;
		if(StringUtils.isEmpty(requestTrackingDiscard) == false){
			this.trackingDiscardUris = this.requestTrackingDiscard.split("\\,"); 			
		}
	}
}